﻿//=============================================================================================
// Szamitogepes grafika hazi feladat keret. Ervenyes 2012-tol.          
// A //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// sorokon beluli reszben celszeru garazdalkodni, mert a tobbit ugyis toroljuk. 
// A beadott program csak ebben a fajlban lehet, a fajl 1 byte-os ASCII karaktereket tartalmazhat. 
// Tilos:
// - mast "beincludolni", illetve mas konyvtarat hasznalni
// - faljmuveleteket vegezni (printf is fajlmuvelet!)
// - new operatort hivni az onInitialization f�ggv�nyt kiv�ve, a lefoglalt adat korrekt felszabad�t�sa n�lk�l 
// - felesleges programsorokat a beadott programban hagyni
// - tovabbi kommenteket a beadott programba irni a forrasmegjelolest kommentjeit kiveve
// ---------------------------------------------------------------------------------------------
// A feladatot ANSI C++ nyelvu forditoprogrammal ellenorizzuk, a Visual Studio-hoz kepesti elteresekrol
// es a leggyakoribb hibakrol (pl. ideiglenes objektumot nem lehet referencia tipusnak ertekul adni)
// a hazibeado portal ad egy osszefoglalot.
// ---------------------------------------------------------------------------------------------
// A feladatmegoldasokban csak olyan gl/glu/glut fuggvenyek hasznalhatok, amelyek
// 1. Az oran a feladatkiadasig elhangzottak ES (logikai AND muvelet)
// 2. Az alabbi listaban szerepelnek:  
// Rendering pass: glBegin, glVertex[2|3]f, glColor3f, glNormal3f, glTexCoord2f, glEnd, glDrawPixels
// Transzformaciok: glViewport, glMatrixMode, glLoadIdentity, glMultMatrixf, gluOrtho2D, 
// glTranslatef, glRotatef, glScalef, gluLookAt, gluPerspective, glPushMatrix, glPopMatrix,
// Illuminacio: glMaterialfv, glMaterialfv, glMaterialf, glLightfv
// Texturazas: glGenTextures, glBindTexture, glTexParameteri, glTexImage2D, glTexEnvi, 
// Pipeline vezerles: glShadeModel, glEnable/Disable a kovetkezokre:
// GL_LIGHTING, GL_NORMALIZE, GL_DEPTH_TEST, GL_CULL_FACE, GL_TEXTURE_2D, GL_BLEND, GL_LIGHT[0..7]
//
// NYILATKOZAT
// ---------------------------------------------------------------------------------------------
// Nev    : Küzdi Máté
// Neptun : DYLP1F
// ---------------------------------------------------------------------------------------------
// ezennel kijelentem, hogy a feladatot magam keszitettem, es ha barmilyen segitseget igenybe vettem vagy 
// mas szellemi termeket felhasznaltam, akkor a forrast es az atvett reszt kommentekben egyertelmuen jeloltem. 
// A forrasmegjeloles kotelme vonatkozik az eloadas foliakat es a targy oktatoi, illetve a 
// grafhazi doktor tanacsait kiveve barmilyen csatornan (szoban, irasban, Interneten, stb.) erkezo minden egyeb 
// informaciora (keplet, program, algoritmus, stb.). Kijelentem, hogy a forrasmegjelolessel atvett reszeket is ertem, 
// azok helyessegere matematikai bizonyitast tudok adni. Tisztaban vagyok azzal, hogy az atvett reszek nem szamitanak
// a sajat kontribucioba, igy a feladat elfogadasarol a tobbi resz mennyisege es minosege alapjan szuletik dontes.  
// Tudomasul veszem, hogy a forrasmegjeloles kotelmenek megsertese eseten a hazifeladatra adhato pontokat 
// negativ elojellel szamoljak el es ezzel parhuzamosan eljaras is indul velem szemben.
//=============================================================================================
 
#include <math.h>
#include <stdlib.h>
 
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
// MsWindows-on ez is kell
#include <windows.h>     
#endif // Win32 platform
 
#include <GL/gl.h>
#include <GL/glu.h>
// A GLUT-ot le kell tolteni: http://www.opengl.org/resources/libraries/glut/
#include <GL/glut.h>     
 
 
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Innentol modosithatod...

const int N = 20;
const float PI = 3.14;
const float radius = 1.0;
const float ballTomeg = 3.14;
const float wallTomeg = 8.4;
float dt = 1.0;
float time = 0.0;
float eps_def = 0.5;

//--------------------------------------------------------
// 3D Vektor
//--------------------------------------------------------
struct Vector {
   float x, y, z;
 
   Vector( ) { 
    x = y = z = 0;
   }
   Vector(float x0, float y0, float z0 = 0) { 
    x = x0; y = y0; z = z0;
   }
   Vector operator*(float a) { 
    return Vector(x * a, y * a, z * a); 
   }
   Vector operator+(const Vector& v) {
     return Vector(x + v.x, y + v.y, z + v.z); 
   }
   Vector operator-(const Vector& v) {
     return Vector(x - v.x, y - v.y, z - v.z); 
   }
   float operator*(const Vector& v) {     // dot product
    return (x * v.x + y * v.y + z * v.z); 
   }
   Vector operator%(const Vector& v) {     // cross product
    return Vector(y*v.z-z*v.y, z*v.x - x*v.z, x*v.y - y*v.x);
   }
   float Length() { return sqrt(x * x + y * y + z * z); }
};
 
//--------------------------------------------------------
// Spektrum illetve szin
//--------------------------------------------------------
struct Color {
   float r, g, b;
 
   Color( ) { 
    r = g = b = 0;
   }
   Color(float r0, float g0, float b0) { 
    r = r0; g = g0; b = b0;
   }
   Color operator*(float a) { 
    return Color(r * a, g * a, b * a); 
   }
   Color operator*(const Color& c) { 
    return Color(r * c.r, g * c.g, b * c.b); 
   }
   Color operator+(const Color& c) {
     return Color(r + c.r, g + c.g, b + c.b); 
   }
};
 
class AABB {
public:
    float xmin, ymin, xmax, ymax;
    Vector origo;
    float vx, vy;
    float sebesseg_x, sebesseg_y;

    AABB(float xmin0, float ymin0, float xmax0, float ymax0){
        xmin = xmin0;
        ymin = ymin0;
        xmax = xmax0;
        ymax = ymax0;
    }
	AABB(Vector origo0){
		origo = origo0;
		xmin = origo.x - 1;
		xmax = origo.x + 1;
		ymin = origo.y - 1;
		ymax = origo.y + 1;
	}
    void Move(float dx, float dy){
        xmin += dx;
        xmax += dx;
        ymin += dy;
        ymax += dy;
    }
    bool Collide(AABB other,float eps){
		bool collide = (xmax + eps > other.xmin  &&
				xmin  < other.xmax + eps &&
				ymax + eps > other.ymin &&
				ymin  < other.ymax + eps);
		if(collide)
			return true;
		return false;
    }
    void Draw () {
        glColor3f(1.0f, 1, 1);
        glBegin(GL_QUADS);
        glVertex2f(xmin, ymin);
        glVertex2f(xmax, ymin);
        glVertex2f(xmax, ymax);
        glVertex2f(xmin, ymax);
        glEnd();
    }
}

topWall(-20, 20, 20, 21),
bottomWall (-20, -21, 20, -20),
rightInvisibleWall(20, -26, 27, 26),
leftInvisibleWall(-20.2f, -26, -20.1f, 26),
topInvisibleWall(-26, 26, 26, 27),
bottomInvisibleWall(-26, -27, 26, -26),

fieldRightWall(20, -21, 21, 21),
fieldLeftWall(-21, -21, -20, 21);
 
class RedWall : public AABB {
public:
	float ballSebessegX;
    RedWall(float xmin, float ymin, float xmax, float ymax) : AABB(xmin, ymin, xmax, ymax) {
		ballSebessegX = 0;
		vx = -1;
	}
    void Draw () {
        glColor3f(1.0f, 0, 0);
		glBegin(GL_QUADS);
        glVertex2f(xmin, ymin);
        glVertex2f(xmax, ymin);
        glVertex2f(xmax, ymax);
        glVertex2f(xmin, ymax);
        glEnd();
    }
	void TimeMove(float dt){
		sebesseg_x = (2*ballTomeg*ballSebessegX)/(wallTomeg+ballTomeg);
		Move(vx * (sebesseg_x/1000) * dt,0);
	}
} 
redWall1(-21, 12.6, -20, 21),
redWall2(-21, 4.2, -20, 12.6),
redWall3(-21, -4.2, -20, 4.2),
redWall4(-21, -12.6, -20, -4.2),
redWall5(-21, -21, -20, -12.6);
 
// mindig a bal also sarkaval hivjuk meg
class Racket : public AABB {
public:
    Racket(float xmin, float ymin) : AABB (xmin, ymin, xmin + 1, ymin + 4) {}
    void Step(float dx, float dy) {
        Move (dx, dy);
        if (topWall.Collide(*this,0) || bottomWall.Collide (*this,0))
            Move(0, -dy);
		if (fieldLeftWall.Collide(*this,0) || fieldRightWall.Collide(*this,0))
            Move(-dx, 0);
    }
    void Draw() {
        glColor3f(0, 1.0f, 0);
        glBegin(GL_QUADS);
        glVertex2f(xmin, ymin);
        glVertex2f(xmax, ymin);
        glVertex2f(xmax, ymax);
        glVertex2f(xmin, ymax);
        glEnd();
    }
}
racket (10, 10);
 
class Ball : public AABB {
	float temp;
public:
    Ball(float x, float y) : AABB (Vector(x, y)) {
        vx = 1;
        vy = 1;
        sebesseg_x = 10;
        sebesseg_y = 2;
		temp = 0;
    }
 
    void Draw() { 
        float angle;
        glColor3f(1.0, 1.0, 0.0);
        glBegin(GL_POLYGON); 
        for(int i = 0; i < 100; i++) {
            angle = i*2*PI/100; 
            glVertex2f(origo.x + (cos(angle) * radius), origo.y + (sin(angle) * radius));
        }
        glEnd();
    }
 
    void TimeMove(float dt) {
        float length = sqrt((vx*vx)+(vy*vy));
		float absSebesseg = sqrt(sebesseg_x * sebesseg_x + sebesseg_y * sebesseg_y) / 1000;
		float epsilon = absSebesseg < eps_def ? absSebesseg : eps_def;
        Move(vx/length*(sebesseg_x/1000)*dt, vy/length*(sebesseg_y/1000)*dt);
        origo.x = xmin + 1;
        origo.y = ymin + 1;
 
        if (topWall.Collide(*this,epsilon) ){
            vy *= -1; 
        }
        else if (bottomWall.Collide(*this,epsilon)) {
            vy *= -1;  
        }
		
        else if (racket.Collide(*this,epsilon)) {	
			if ((origo.x +epsilon < racket.xmin && origo.y > racket.ymax + epsilon) ||
				(origo.x > racket.xmax + epsilon && origo.y + epsilon < racket.ymin)){
				temp = vx;
				vx = vy;
				vy = temp;
			}
			else if ((origo.x > racket.xmax+epsilon && origo.y > racket.ymax+epsilon) ||
					 (origo.x +epsilon< racket.xmin && origo.y+epsilon < racket.ymin)){
				temp = vx;
				vx = -vy;
				vy = -temp;
			}
			else if((origo.x +epsilon< racket.xmin && origo.y+epsilon < racket.ymax && origo.y > ymin+epsilon) ||
					(origo.x > racket.xmax && origo.y +epsilon< racket.ymax && origo.y > ymin+epsilon)){
				vx *= -1;
			}
			else if((origo.y > racket.ymax+epsilon && origo.x+epsilon < racket.xmax && origo.x > xmin+epsilon) ||
				    (origo.y+epsilon < racket.ymin && origo.x+epsilon < racket.xmax && origo.x > xmin+epsilon)){
				vy *= -1;
			}
			sebesseg_x = sebesseg_x * sqrt(1.2);
			sebesseg_y = sebesseg_y * sqrt(1.2);
		}
		
		else if (redWall1.Collide(*this,epsilon)) {
			redWall1.ballSebessegX = sebesseg_x;
			vx *= -1;
			sebesseg_x = abs(((ballTomeg-wallTomeg)*sebesseg_x) / (ballTomeg+wallTomeg));
		}
		else if (redWall2.Collide(*this,epsilon)){
			redWall2.ballSebessegX = sebesseg_x;
			vx *= -1;
			sebesseg_x = abs(((ballTomeg-wallTomeg)*sebesseg_x) / (ballTomeg+wallTomeg));
		}
		else if (redWall3.Collide(*this,epsilon)){
			redWall3.ballSebessegX = sebesseg_x;
			vx *= -1;
			sebesseg_x = abs(((ballTomeg-wallTomeg)*sebesseg_x) / (ballTomeg+wallTomeg));
		}
		else if (redWall4.Collide(*this,epsilon)){
			redWall4.ballSebessegX = sebesseg_x;
			vx *= -1;
			sebesseg_x = abs(((ballTomeg-wallTomeg)*sebesseg_x) / (ballTomeg+wallTomeg));
		}
		else if (redWall5.Collide(*this,epsilon)){
			redWall5.ballSebessegX = sebesseg_x;
			vx *= -1;
			sebesseg_x = abs(((ballTomeg-wallTomeg)*sebesseg_x) / (ballTomeg+wallTomeg));
		}
		else if (rightInvisibleWall.Collide(*this,epsilon)){
            vx *= -1;   
        }
		else if (leftInvisibleWall.Collide(*this,epsilon)){
            vx *= -1;   
        }
		else if(bottomInvisibleWall.Collide(*this,epsilon)) {
			vy *= -1;
		}
		else if(topInvisibleWall.Collide(*this,epsilon)) {
			vy *= -1;
		}
    }
}
ball(0.0,0.0);
 
void SimulateWorld(float tstart,float tend){
    float ts;
    for(ts = tstart; ts < tend; ts += dt) {
        float te = (tend > (ts + dt) ? (ts + dt) : tend);

		redWall1.TimeMove(te-ts);
		redWall2.TimeMove(te-ts);
		redWall3.TimeMove(te-ts);
		redWall4.TimeMove(te-ts);
		redWall5.TimeMove(te-ts);
		
        ball.TimeMove(te-ts);
    }
}

// `Idle' esemenykezelo, jelzi, hogy az ido telik, az Idle esemenyek frekvenciajara csak a 0 a garantalt minimalis ertek
void onIdle( ) {
    float old_time = time;
    time = glutGet(GLUT_ELAPSED_TIME);
    SimulateWorld(old_time,time);
    glutPostRedisplay();
}
 
// Inicializacio, a program futasanak kezdeten, az OpenGL kontextus letrehozasa utan hivodik meg (ld. main() fv.)
void onInitialization( ) { 
}
 
// Rajzolas, ha az alkalmazas ablak ervenytelenne valik, akkor ez a fuggveny hivodik meg
void onDisplay( ) {
	glClearColor(0, 0, 0, 1.0f);        
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 
    glViewport(0, 0, 600, 600);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(-26, 26, -26, 26);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
 
    topWall.Draw();
    bottomWall.Draw();
    redWall1.Draw();
    redWall2.Draw();
    redWall3.Draw();
    redWall4.Draw();
    redWall5.Draw();
    racket.Draw();
    ball.Draw();
	
    glutSwapBuffers();                    
 
}
 
// Billentyuzet esemenyeket lekezelo fuggveny
void onKeyboard(unsigned char key, int x, int y) {
    switch(key){
        case 'i' : 
			racket.Step(0,1); break;
        case 'm' : 
			racket.Step(0,-1); break;
        case 'j' :
			racket.Step(-1,0); break;
        case 'k' : 
			racket.Step(1,0); break;
    }
    glutPostRedisplay();
}
 
// Eger esemenyeket lekezelo fuggveny
void onMouse(int button, int state, int x, int y) {                         // Ilyenkor rajzold ujra a kepet
}
 
// ...Idaig modosithatod
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
// A C++ program belepesi pontja, a main fuggvenyt mar nem szabad bantani
int main(int argc, char **argv) {
    glutInit(&argc, argv);                 // GLUT inicializalasa
    glutInitWindowSize(600, 600);            // Alkalmazas ablak kezdeti merete 600x600 pixel 
    glutInitWindowPosition(100, 100);            // Az elozo alkalmazas ablakhoz kepest hol tunik fel
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);    // 8 bites R,G,B,A + dupla buffer + melyseg buffer
 
    glutCreateWindow("Grafika hazi feladat");        // Alkalmazas ablak megszuletik es megjelenik a kepernyon
 
    glMatrixMode(GL_MODELVIEW);                // A MODELVIEW transzformaciot egysegmatrixra inicializaljuk
    glLoadIdentity();
    glMatrixMode(GL_PROJECTION);            // A PROJECTION transzformaciot egysegmatrixra inicializaljuk
    glLoadIdentity();
 
    onInitialization();                    // Az altalad irt inicializalast lefuttatjuk
 
    glutDisplayFunc(onDisplay);                // Esemenykezelok regisztralasa
    glutMouseFunc(onMouse); 
    glutIdleFunc(onIdle);
    glutKeyboardFunc(onKeyboard);
 
    glutMainLoop();                    // Esemenykezelo hurok
    
    return 0;
}